package com.hucheng.seckill.service.impl;

import com.alibaba.dubbo.config.annotation.Service;
import com.alibaba.fastjson.JSONObject;
import com.hucheng.common.constant.RedisConstant;
import com.hucheng.common.exception.EduException;
import com.hucheng.common.page.CommonPager;
import com.hucheng.common.page.PageParameter;
import com.hucheng.common.utils.RedisUtils;
import com.hucheng.seckill.entity.SeckillGoodsDO;
import com.hucheng.seckill.mapper.SeckillGoodsMapper;
import com.hucheng.seckill.query.SeckillGoodsQuery;
import com.hucheng.seckill.rabbitmq.SeckillSender;
import com.hucheng.seckill.service.OrderService;
import com.hucheng.seckill.service.SeckillGoodsService;
import com.hucheng.seckill.service.SeckillOrderService;
import com.hucheng.seckill.vo.OrderInfoVO;
import com.hucheng.seckill.vo.SeckillGoodsVO;
import com.hucheng.seckill.vo.SeckillOrderVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * @Author: 凉白开不加冰
 * @Version: 0.0.1V
 * @Date: 2019-06-25
 * @Description: 秒杀商品列表业务
 **/
@Slf4j
@Component
@Service(interfaceClass = SeckillGoodsService.class, retries = -1, timeout = 5000)
public class SeckillGoodsServiceImpl implements SeckillGoodsService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private SeckillGoodsMapper seckillGoodsMapper;

    @Autowired
    private OrderService orderService;

    @Autowired
    private SeckillOrderService seckillOrderService;

    @Autowired
    private SeckillSender seckillSender;

    /**
     * 缓存记录商品库存秒杀完
     */
    private Map<String, Boolean> localOverMap = new LinkedHashMap<>();

    @Override
    public CommonPager<SeckillGoodsVO> listByPage(SeckillGoodsQuery seckillGoodsQuery) {
        PageParameter pageParameter = seckillGoodsQuery.getPageParameter();

        List<SeckillGoodsDO> lists = seckillGoodsMapper.selectByQuery(seckillGoodsQuery);
        List<SeckillGoodsVO> results = new ArrayList<>();
        for (SeckillGoodsDO seckillGoodsDO : lists) {

            seckillGoodsDO.setStatus(getSeckillGoodsStatus(seckillGoodsDO.getDateStart(), seckillGoodsDO.getDateEnd()));

            results.add(SeckillGoodsVO.buildSeckillGoodsVO(seckillGoodsDO));
        }

        return new CommonPager<>(new PageParameter(
                pageParameter.getCurrentPage(),
                pageParameter.getPageSize(),
                seckillGoodsMapper.countByQuery(seckillGoodsQuery)),
                results);
    }

    @Override
    public SeckillGoodsVO getSeckillGoodsByGoodsId(Integer goodsId) {

        SeckillGoodsDO seckillGoodsDO = seckillGoodsMapper.getSeckillGoodsByGoodsId(goodsId);

        seckillGoodsDO.setStatus(getSeckillGoodsStatus(seckillGoodsDO.getDateStart(), seckillGoodsDO.getDateEnd()));

        return SeckillGoodsVO.buildSeckillGoodsVO(seckillGoodsDO);
    }

    @Override
    public void seckillGoods(Integer userId, Integer goodsId) throws EduException {
        if (localOverMap.get(goodsId + "") != null && localOverMap.get(goodsId + "")) {
            setGoodsOver(goodsId);
            throw new EduException(130000);
        }

        //判断用户是否秒杀到
        SeckillOrderVO seckillOrderVO = RedisUtils.get(redisTemplate, RedisConstant.SECKILL_ORDER_KEY + userId + "_" + goodsId, SeckillOrderVO.class);
        //从数据库获取订单判断
        SeckillOrderVO seckillOrderByDBVO = seckillOrderService.getUserSeckillOrderByUserId(userId, goodsId);
        if (seckillOrderVO != null || seckillOrderByDBVO != null) {
            log.info("==> 秒杀商品 " + goodsId + " 用户已抢到");
            throw new EduException(130001);
        }

        //预减库存
        long stockCount = RedisUtils.decr(redisTemplate, RedisConstant.SECKILL_GOODS_KEY + goodsId, 1);
        if (stockCount < 0) {
            localOverMap.put(goodsId + "", true);
            throw new EduException(130000);
        }

        //入队列进行排队
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("userId", userId);
        jsonObject.put("goodsId", goodsId);

        seckillSender.send(jsonObject);
        log.info("==> 秒杀商品 " + goodsId + " 用户 " + userId + " 进入队列");
    }

    @Override
    public void initSeckillGoods() {
        List<SeckillGoodsDO> lists = seckillGoodsMapper.selectByQuery(new SeckillGoodsQuery());
        for (SeckillGoodsDO seckillGoodsDO : lists) {
            RedisUtils.set(redisTemplate, RedisConstant.SECKILL_GOODS_KEY + seckillGoodsDO.getGoodsId(), seckillGoodsDO.getStockCount() + "");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createSeckillOrder(Integer userId, SeckillGoodsVO seckillGoodsVO) {
        //减库存成功生成订单信息
        int ret = seckillGoodsMapper.reduceStock(seckillGoodsVO.getGoodsId());
        if (ret > 0) {
            //创建订单
            OrderInfoVO orderVO = orderService.createOrder(userId, seckillGoodsVO);
            //创建秒杀订单
            SeckillOrderVO seckillOrderVO = seckillOrderService.createSeckillOrder(userId, seckillGoodsVO.getGoodsId(), orderVO.getOrderId());
            seckillOrderVO.setOrderId(orderVO.getOrderId());
            //创建redis订单
            RedisUtils.set(redisTemplate, RedisConstant.SECKILL_ORDER_KEY + userId + "_" + seckillGoodsVO.getGoodsId(), JSONObject.toJSONString(seckillOrderVO));
        } else {
            setGoodsOver(seckillGoodsVO.getGoodsId());
        }

    }

    @Override
    public Integer seckillResult(Integer userId, Integer goodsId) {
        SeckillOrderVO seckillOrderVO = RedisUtils.get(redisTemplate, RedisConstant.SECKILL_ORDER_KEY + userId + "_" + goodsId, SeckillOrderVO.class);
        if (seckillOrderVO != null) {
            return seckillOrderVO.getSeckillOrderId();
        } else {
            boolean isOver = getGoodsOver(goodsId);
            if (isOver) {
                //结束
                return -1;
            } else {
                //排队
                return 0;
            }
        }
    }

    private void setGoodsOver(Integer goodsId) {
        log.info("==> 秒杀商品 " + goodsId + " 已抢完,设置状态为 true");
        RedisUtils.set(redisTemplate, RedisConstant.SECKILL_GOODS_STATUS + goodsId, "true");
    }

    private boolean getGoodsOver(Integer goodsId) {
        return RedisUtils.hasKey(redisTemplate, RedisConstant.SECKILL_GOODS_STATUS + goodsId);
    }

    /**
     * 根据时间判断秒杀商品状态
     *
     * @param start
     * @param end
     * @return
     */
    private Integer getSeckillGoodsStatus(Date start, Date end) {
        Date date = new Date();
        if (date.getTime() < start.getTime()) {
            //秒杀未开始
            return 1;
        } else if (date.getTime() > end.getTime()) {
            //秒杀结束
            return 3;
        } else {
            //秒杀进行中
            return 2;
        }
    }
}
